using Godot;

public partial class Player : CharacterBody3D
{
	const float EYE_HEIGHT_STAND = 1.6f;
	const float EYE_HEIGHT_CROUCH = 1.4f;

	const float MOVEMENT_SPEED_GROUND = 0.6f;
	const float MOVEMENT_SPEED_AIR = 0.11f;
	const float MOVEMENT_SPEED_CROUCH_MODIFIER = 0.5f;
	const float MOVEMENT_FRICTION_GROUND = 0.9f;
	const float MOVEMENT_FRICTION_AIR = 0.98f;

	Vector2 mouseMotion = new Vector2();
	uint selectedBlock = 6;

	private Node3D head;
	private RayCast3D raycast;
	private Camera3D cameraAttributes;
	private TextureRect selectedBlockTexture;
	private VoxelWorld voxelWorld;
	private CenterContainer crosshair;
	private MeshInstance3D aimPreview;
	private Node settings;
	private ProgressBar progressBar;
	private float gravity;
	private double t = 0;
	public override void _Ready()
	{
		head = GetNode<Node3D>("Head");
		raycast = GetNode<RayCast3D>("Head/RayCast3D");
		cameraAttributes = GetNode<Camera3D>("Head/Camera3D");
		selectedBlockTexture = GetNode<TextureRect>("SelectedBlock");
		voxelWorld = GetNode<VoxelWorld>("../VoxelWorld");
		crosshair = GetNode<CenterContainer>("../PauseMenu/Crosshair");
		aimPreview = GetNode<MeshInstance3D>("AimPreview");
		gravity = (float)ProjectSettings.GetSetting("physics/3d/default_gravity");
		settings = GetNode<Node>("/root/Settings");
		progressBar = GetNode<ProgressBar>("ProgressBar");
		Input.MouseMode = Input.MouseModeEnum.Captured;
	}

	public override void _Process(double delta)
	{
		mouseMotion.Y = Mathf.Clamp(mouseMotion.Y, -1560, 1560);
		Basis = Basis.FromEuler(new Vector3(0, mouseMotion.X * -0.001f, 0));
		head.Basis = Basis.FromEuler(new Vector3(mouseMotion.Y * -0.001f, 0, 0));

		var rayPosition = raycast.GetCollisionPoint();
		var rayNormal = raycast.GetCollisionNormal();
		if (Input.IsActionJustReleased("throw"))
		{
			t = 0;
			progressBar.Value = 0;
			GD.Print(t);
			BoomBody3d.Throw(GetTree(), Transform * head.Transform * cameraAttributes.Transform);
		}
		if (Input.IsActionJustPressed("throw"))
		{
			t += delta;

		}
		if (t != 0)
		{
			GD.Print(t);
			
			progressBar.Value = t / 2;
			progressBar.Modulate = new Color((float)progressBar.Value,(float)(1-progressBar.Value),0);
			t += delta;
		}

		if (Input.IsActionJustPressed("pick_block"))
		{
			var blockGlobalPosition = (Vector3)(rayPosition - rayNormal / 2).Floor();
			var blockSubPosition = (Vector3)blockGlobalPosition.PosMod(16);
			var chunkPosition = (Vector3)(blockGlobalPosition - blockSubPosition) / 16;
			selectedBlock = voxelWorld.GetBlockInChunk((Vector3I)chunkPosition, (Vector3I)blockSubPosition);
		}
		else
		{
			if (Input.IsActionJustPressed("prev_block"))
			{
				selectedBlock -= 1;
			}
			if (Input.IsActionJustPressed("next_block"))
			{
				selectedBlock += 1;
			}
			selectedBlock = (uint)Mathf.Wrap(selectedBlock, 1, 30);
		}
		var uv = Chunk.CalculateBlockUvs(selectedBlock);
		((AtlasTexture)selectedBlockTexture.Texture).Region = new Rect2(uv[0] * 512, Vector2.One * 64);

		if (crosshair.Visible && raycast.IsColliding())
		{
			aimPreview.Visible = true;
			var rayCurrentBlockPosition = (Vector3I)(rayPosition - rayNormal / 2).Floor();
			aimPreview.GlobalPosition = (Vector3)rayCurrentBlockPosition + new Vector3(0.5f, 0.5f, 0.5f);
			var breaking = Input.IsActionJustPressed("break");
			var placing = Input.IsActionJustPressed("place");

			if (breaking == placing)
			{
				return;
			}

			if (breaking)
			{
				var blockGlobalPosition = rayCurrentBlockPosition;
				voxelWorld.SetBlockGlobalPosition(blockGlobalPosition, 0);
			}
			else if (placing)
			{
				var blockGlobalPosition = (Vector3I)(rayPosition + rayNormal / 2).Floor();
				voxelWorld.SetBlockGlobalPosition(blockGlobalPosition, selectedBlock);
			}
		}
		else
		{
			aimPreview.Visible = false;
		}
	}

	public override void _PhysicsProcess(double delta)
	{
		((CameraAttributesPractical)cameraAttributes.Attributes).DofBlurFarEnabled = (bool)settings.Get("fog_enabled");
		((CameraAttributesPractical)cameraAttributes.Attributes).DofBlurFarDistance = (float)settings.Get("fog_distance") * 1.5f;
		((CameraAttributesPractical)cameraAttributes.Attributes).DofBlurFarTransition = (float)settings.Get("fog_distance") * 0.125f;

		var crouching = Input.IsActionJustPressed("crouch");

		var deltaY = (float)Mathf.Lerp(head.Transform.Origin.Y, crouching ? EYE_HEIGHT_CROUCH : EYE_HEIGHT_STAND, 16f * delta);
		head.Position = head.Position with { Y=deltaY };

		var movementVec2 = Input.GetVector("move_left", "move_right", "move_forward", "move_back");
		var movement = Basis * new Vector3(movementVec2.X, 0, movementVec2.Y);

		if (IsOnFloor())
		{
			movement *= MOVEMENT_SPEED_GROUND;
		}
		else
		{
			movement *= MOVEMENT_SPEED_AIR;
		}

		if (crouching)
		{
			movement *= MOVEMENT_SPEED_CROUCH_MODIFIER;
		}
		
		Velocity = Velocity with { Y = Velocity.Y - (float)(gravity * delta) };

		Velocity += new Vector3(movement.X, 0, movement.Z);

		var a = IsOnFloor() ? MOVEMENT_FRICTION_GROUND : MOVEMENT_FRICTION_AIR;
		Velocity = Velocity with { X = Velocity.X * a };
		Velocity = Velocity with { Z = Velocity.Z * a };
		MoveAndSlide();

		if (IsOnFloor() && Input.IsActionJustPressed("jump"))
		{
			Velocity = Velocity with { Y = 7.5f };
		}
	}

	public override void _Input(InputEvent e)
	{
		if (e is InputEventMouseMotion)
		{
			if (Input.MouseMode == Input.MouseModeEnum.Captured)
			{
				mouseMotion += ((InputEventMouseMotion)e).Relative;
			}
		}
	}

	public Vector3I ChunkPos()
	{
		return (Vector3I)(Position / VoxelWorld.CHUNK_SIZE).Floor();
	}

}
